<?php

/**
 * Fete_View
 * @author Nguyen Duc Thuan <me@ndthuan.com>
 * @copyright Nguyen Duc Thuan <me@ndthuan.com>
 */
class Fete_View
{
    protected $_variables = array();

    protected $_scriptName = null;

    protected $_scriptExtension = 'phtml';

    protected $_scriptDirectory = null;

    protected $_helperDirectories = array();

    static protected $_loadedHelpers = array();

    /**
     *
     * Assign variable to view
     * @param $var
     * @param $value
     * @return Fete_View
     */
    public function &assign($var, $value = null)
    {
        if (is_array($var)) {
            foreach ($var as $name => $value)
            {
                $this->_variables[$name] = $value;
            }
        } elseif ($value instanceof Fete_View) {
            $this->_variables[$var] = $value->fetch();
        } else {
            $this->_variables[$var] = $value;
        }

        return $this;
    }

    /**
     * Get variable from view
     * @param $var
     * @return mixed
     */
    public function getValue($var)
    {
        if (isset($this->_variables[$var])) {
            return $this->_variables[$var];
        }

        return null;
    }

    /**
     *
     * Set script name
     * @param $scriptName
     * @return Fete_View
     */
    public function &setScriptName($scriptName)
    {
        $this->_scriptName = $scriptName;

        return $this;
    }

    /**
     *
     * Set script directory
     * @param $directory
     * @return Fete_View
     */
    public function &setDirectory($directory)
    {
        $this->_scriptDirectory = $directory;

        return $this;
    }

    /**
     *
     * Get current script directory
     * @return string
     */
    public function getDirectory()
    {
        return $this->_scriptDirectory;
    }

    /**
     *
     * @return string
     */
    public function getScriptExtension()
    {
        return $this->_scriptExtension;
    }

    /**
     *
     * set script extension (default: 'phtml')
     * @param $extension
     * @return Fete_View
     */
    public function &setScriptExtension($extension)
    {
        $this->_scriptExtension = $extension;

        return $this;
    }

    /**
     *
     * Get script name
     * @return string
     */
    public function getScriptName()
    {
        return $this->_scriptName;
    }

    /**
     *
     * Display a script
     * @param $scriptName
     * @return Fete_View
     */
    public function &display($scriptName = null)
    {
        if (null === $this->_scriptDirectory) {
            throw new Fete_View_Exception('Script directory is not set');
            return false;
        } elseif (null === $scriptName && null === $this->_scriptName) {
            throw new Fete_View_Exception('Script is not set');
            return false;
        } elseif (null !== $scriptName) {
            $this->_scriptName = $scriptName;
        }

        $fileName = $scriptName . '.' . $this->_scriptExtension;

        if (!file_exists($this->_scriptDirectory . DIRECTORY_SEPARATOR . $fileName)) {
            throw new Fete_View_Exception('Script ' . $fileName . ' does not exist in ' . $this->_scriptDirectory);
            return false;
        }

        extract($this->_variables);

        require $this->_scriptDirectory . DIRECTORY_SEPARATOR . $fileName;

        return $this;
    }

    /**
     * Fetch output of a script
     * @param $scriptName script name
     * @return string
     */
    public function fetch($scriptName = null)
    {
        ob_start();
        $this->display($scriptName);
        return ob_get_clean();
    }

    /**
     *
     * @param string $scriptName
     * @return Fete_View
     */
    public function &attach($scriptName)
    {
        $scriptFile = $scriptName . '.' . $this->_scriptExtension;
        $file = $this->_scriptDirectory . DIRECTORY_SEPARATOR . $scriptFile;

        if (!file_exists($file)) {
            throw new Fete_View_Helper_Exception('Attached script '
                . $scriptFile
                . ' does not exist in ' . $this->_scriptDirectory);
            return false;
        }

        require $file;

        return $this;
    }

    /**
     *
     * Add helpers directory
     * @param $path
     * @param $prefix
     * @return Fete_View
     */
    public function &addHelpersDirectory($path, $prefix = 'Fete_View_Helper')
    {
        array_unshift($this->_helperDirectories, array($path, $prefix));

        return $this;
    }

    /**
     *
     * Construct the object
     * @todo replace dirname(__FILE__) by __DIR__ for PHP 5.3
     */
    public function __construct($scriptDir = null)
    {
        Fete_View_Helper::$view = $this;
        $this->_scriptDirectory = $scriptDir;
        $this->_helperDirectories[] = array(dirname(__FILE__) 
            . DIRECTORY_SEPARATOR
            . 'View' . DIRECTORY_SEPARATOR . 'Helper', 'Fete_View_Helper');
        $this->docType('XHTML 1.0 TRANSITIONAL');
    }

    public function __call($name, $args)
    {
        if (isset(self::$_loadedHelpers[$name])) {
            return call_user_func_array(array(
                self::$_loadedHelpers[$name]
                , $name)
            , $args);
        }

        $helperName     = ucfirst($name);
        $helperFileName = $helperName . '.php';

        foreach ($this->_helperDirectories as $helperConfig)
        {
            $helperFilePath = $helperConfig[0] . DIRECTORY_SEPARATOR
                . $helperFileName;

            if (file_exists($helperFilePath)) {
                require_once $helperFilePath;
                $helperClassName    = $helperConfig[1] . '_' . $helperName;
                if (class_exists($helperClassName, false)) {
                    $helperInstance = new $helperClassName();
                    self::$_loadedHelpers[$name] = &$helperInstance;
                    return call_user_func_array(array($helperInstance, $name)
                        , $args);
                }
            }
        }

        throw new Fete_View_Exception('No helpers for ' . $name . ' found');
        return false;
    }

    public function __set($var, $value)
    {
        $this->assign($var, $value);
    }

    public function __get($var)
    {
        return $this->getValue($var);
    }

    public function __isset($var)
    {
        return isset($this->_variables[$var]);
    }

    public function __unset($var)
    {
        unset($this->_variables[$var]);
    }

    public function __toString()
    {
        return $this->fetch();
    }
}
